#include "gtest/gtest.h"
#include "../BaseUtil/MyTest.h"
#include "DOMCoreImp.h"
namespace DOMCoreImp {

class NodeListImpTest : public testing::Test {
protected:
	virtual void SetUp() { }
	virtual void TearDown() { }

	class DummyNodeImp : public NodeImp {
	public:
		DummyNodeImp() : NodeImp("DUMMY_NODE", NULL) {}
		virtual short unsigned int getNodeType() { return 99; }
	};

	static bool isThereNode(NodeListPtr nodeList, Node * node) {
		for(unsigned long iPos = 0; iPos < nodeList->getLength(); ++iPos)
			if( node == nodeList->item(iPos) )
				return true;
		return false;
	}

	static bool isThereEveryNode(NodeListPtr nodeList, vector< Node * > & candidateNodes) {
		vector< Node * >::const_iterator iterCandidate = candidateNodes.begin();
		for(; iterCandidate != candidateNodes.end(); ++iterCandidate) {
			if( false == isThereNode(nodeList, *iterCandidate) )
				return false;
		}
		return true;
	}

	static bool insertNode(NodeListImpPtr nodeList, unsigned long howManyNode, unsigned long pos) {
		const unsigned long expectedFinalLength = nodeList->getLength() + howManyNode;
		vector< Node * > newNodes;

		for(unsigned long i = 0; i < howManyNode; ++i) {
			Node * newNode = new DummyNodeImp();
			newNodes.push_back( newNode );
			unsigned int lengthBeforeInsert = nodeList->getLength();

			Node * nodeFromInsert = nodeList->insert(pos +i, newNode);

			if( lengthBeforeInsert +1 != nodeList->getLength() ) return false;
			if( newNode != nodeList->item(pos +i) ) return false;
			if( nodeFromInsert != newNode ) return false;
		}

		if( false == isThereEveryNode( nodeList, newNodes ) ) return false;
		if( expectedFinalLength != nodeList->getLength() ) return false;
		return true;
	}

	static bool removeNode(NodeListImpPtr nodeList, unsigned long fromPos, unsigned long toPos) {
		const unsigned long expectedFinalLength = nodeList->getLength() - (toPos - fromPos);

		for(unsigned long iRemoveNode = 0; iRemoveNode < toPos - fromPos; ++iRemoveNode) {
			Node * victim = nodeList->item(fromPos);
			const unsigned int lengthBeforeRemove = nodeList->getLength();

			Node * nodeFromRemove = nodeList->remove(fromPos);

			if( lengthBeforeRemove -1 != nodeList->getLength() ) return false;
			if( isThereNode(nodeList, victim) ) return false;
			if( nodeFromRemove != victim ) return false;
		}

		if( nodeList->getLength() != expectedFinalLength ) return false;
		return true;
	}

};

TEST_F(NodeListImpTest, Inheritance) {
	NodeListPtr( new NodeListImp() );
}

TEST_F(NodeListImpTest, InsertAndRemove)
{
	const unsigned long maxPreExistNodes[] = { 0, 0, 5, 5, 5 };
	const unsigned long maxNewNodes[]      = { 1, 3, 1, 1, 2 };
	const unsigned long insertPoses[]      = { 0, 0, 2, 5, 2 };

	const unsigned int maxInputs = sizeof(maxPreExistNodes)/sizeof(unsigned long);
	for(unsigned int idx = 0; idx < maxInputs; ++idx)
	{
		const unsigned long maxPreExistNode = maxPreExistNodes[idx];
		const unsigned long maxNewNode = maxNewNodes[idx];
		const unsigned long insertPos = insertPoses[idx];

		// create a list
		NodeListImpPtr namedList = new NodeListImp();
		ASSERT_EQ(0ul, namedList->getLength()) << idx;

		// populate
		const bool rstPopulate = insertNode(namedList, maxPreExistNode, 0);
		ASSERT_TRUE( rstPopulate ) << idx;

		// insert
		const bool rstInsert = insertNode(namedList, maxNewNode, insertPos);
		ASSERT_TRUE( rstInsert ) << idx;

		// remove
		const bool bRemove = removeNode( namedList, insertPos, insertPos + maxNewNode );
		ASSERT_TRUE( bRemove ) << idx;
	}
}

}
